from vtk import *
import math
import copy

class FormalConcept:
	def __init__(self,id):
		self.id = id
		self.extent = []
		self.intent = []
		self.parents = []
		self.children = []
		self.stability = 0
		self.type = None
		self.level = 0

	def SetExtent(self,extent):
		self.extent = extent

	def SetIntent(self,intent):
		self.intent = intent

	def GetExtent(self):
		return self.extent

	def GetIntent(self):
		return self.intent

	def GetCleanIntent(self,output='a'):
		rs = copy.copy(self.intent)
		for c in self.parents:
			for a in c.GetIntent():
				if a in rs:
					del rs[rs.index(a)]
		if output == 'a':
			return rs
		elif output == 's':
			op = ''
			for i in rs:
				op += i
			return op

	def GetId(self):
		return self.id

	def SetChildren(self,concepts):
		self.children = concepts

	def GetChildren(self):
		return self.children

	def SetParents(self,concepts):
		self.parents = concepts

	def GetParents(self):
		return self.parents

	def GetSupport(self):
		return len(self.extent)

	def Summary(self):
		s = 'Extent: ' + str(self.extent) + '\n'
		s += 'Intent: ' + str(self.intent)+ '\n'
		return s

class ConceptLattice:
	DENSITY = 0
	MEAN_ATTRIBUTES = 1
	TIME_USED = 2
	
	def __init__(self):
		self.concepts = {}
		self.top = None
		self.bottom = None
		self.levels = {}
		self.features = {}
	
	def SetLatticeFeature(self,name,value):
		self.features[name] = value

	def GetConceptsByLevel(self,level):
		return self.levels[level]
	
	def Concept(self,key):
		if key not in self.concepts:
			self.concepts[key] = FormalConcept(key)
		return self.concepts[key]
	
	def SetConceptStability(self,key,stability):
		self.concepts[key].stability = stability
	
	def SetConceptLevel(self,key,lev):
		self.concepts[key].level = lev
		if lev not in self.levels:
			self.levels[lev] = []
		self.levels[lev].append(self.concepts[key])
	
	def SetConceptExtent(self,key,objs):
		self.concepts[key].SetExtent(objs)
	
	def SetConceptIntent(self,key,ats):
		self.concepts[key].SetIntent(ats)
	
	def SetConceptParents(self,key,pars):		
		self.concepts[key].SetParents([self.Concept(i) for i in pars])
	
	def SetConceptChildren(self,key,chs):
		self.concepts[key].SetChildren([self.Concept(i) for i in chs])
	
	def SetConcepType(self,key,type):
		self.concepts[key].type = type
		if type == 'top':
			self.top = self.concepts[key]
		elif type == 'bottom':
			self.bottom = self.concepts[key]

	def GetNumberOfConcepts(self):
		return len(self.concepts)
	
	def GetKeys(self):
		return self.concepts.keys()

	def GetConcept(self,key):
		return self.concepts[key]

	def GetNumberOfLevels(self):
		return len(self.levels)
	

'''
Reads a lattice from Coron output.
'''
def readLattice(lattice):
	f = open(lattice, "r")
	__lineN = 3
	counter = 0
	CL = ConceptLattice()
	for line in f:
		if not (line.startswith("#") or line.startswith("Top") or line.startswith("Bottom") or line.startswith("\n") or "generators" in line):
			counter += 1
			if counter%__lineN==1: # Info intent extent
				# TYPE
				type = line[line.index("type=")+5:line.index(", level")]
				# Intent
				intent = line[line.index("{")+1:line.index("}")]
				if intent == '':
					intent = 'none'
				#print 'intent',intent
				#print intent
				if type == "inner" or type=='bottom':
					CL.Concept(intent)
					# Extent
					CL.SetConceptExtent(intent,[o.strip() for o in line[line.index("[")+1:line.index("]")].split(",")])
					# Intent
					CL.SetConceptIntent(intent,[i.strip() for i in intent.split(",")])
					# Level
					CL.SetConceptLevel(intent,int(line[line.index("level=")+6:line.index("stability=")].replace(", ","")))
					# Stability
					stab = line[line.rfind("=")+1:]
					stab = float(stab.replace("]\n",""))
					CL.SetConceptStability(intent,stab)
				elif type == "top":
					intent = "none"
					CL.Concept(intent)
					CL.SetConceptExtent(intent,[o.strip() for o in line[line.index("[")+1:line.index("]")].split(",")])
					CL.SetConceptStability(intent,1.0)
					CL.SetConceptLevel(intent,0)
			CL.SetConcepType(intent,type)
			
			if counter%__lineN==2: # Information of parents
				parent = line[line.index("[")+1:line.index("]")]
				parent = parent.split("}, ")
				ents = []
				for c in range(len(parent)):
					par = parent[c].replace("{","").replace("}","").strip()
					if par != "":
						ents.append(par)
				CL.SetConceptParents(intent,ents)
			
			if counter%__lineN==0: # Information of children
				children = line[line.index("[")+1:line.index("]")]
				children = children.split("}, ")
				ents = []
				for c in range(len(children)):
					ch = children[c].replace("{","").replace("}","").strip()
					if ch != "":
						ents.append(ch)
				CL.SetConceptChildren(intent,ents)
		
		else:
			if line.startswith("# Number of attributes in average:"):
				CL.SetLatticeFeature(CL.MEAN_ATTRIBUTES,float(line.replace("# Number of attributes in average:","").replace("\n","").strip().replace(",",".")))
			if line.startswith("# Density:"):
				CL.SetLatticeFeature(CL.DENSITY,float(line.replace("# Density:","").replace("\n","").replace("%","").strip().replace(",",".")))
			if line.startswith("# Summary:"):
				CL.SetLatticeFeature(CL.TIME_USED,float(line.replace("# Summary:","").replace("sec.","").replace("\n","").strip().replace(",",".")))
	f.close()
	return CL


def create_lattice_vertices(graph,lattice):
	print "Creating Vertices..."
	
	# ARRAY WITH LABELS TO DISPLAY ON THE GRAPH
	vertex_id_array = vtkIdTypeArray()
	vertex_id_array.SetName("id")
	vertex_id_array.SetNumberOfTuples(lattice.GetNumberOfConcepts())
	
	# ARRAY WITH LABELS TO DISPLAY ON THE GRAPH
	vertex_label_array = vtkStringArray()
	vertex_label_array.SetName("names")
	vertex_label_array.SetNumberOfTuples(lattice.GetNumberOfConcepts())
	
	# ARRAY WITH LABELS TO DISPLAY ON THE GRAPH
	intent_array = vtkStringArray()
	intent_array.SetName("intent")
	intent_array.SetNumberOfTuples(lattice.GetNumberOfConcepts())
	
	# COORDINATES ARRAY
	points = vtkPoints()
	
	# STABILITY ARRAY
	stab_vertex_array = vtkFloatArray()
	stab_vertex_array.SetName("stability")
	stab_vertex_array.SetNumberOfTuples(lattice.GetNumberOfConcepts())
	
	# SUPPORT ARRAY
	sup_vertex_array = vtkIntArray()
	sup_vertex_array.SetName("support")
	sup_vertex_array.SetNumberOfTuples(lattice.GetNumberOfConcepts())
	
	# SIZE ARRAY
	size_vertex_array = vtkFloatArray()
	size_vertex_array.SetName("size")
	size_vertex_array.SetNumberOfTuples(lattice.GetNumberOfConcepts())
	
	vertex = {}
	
	# MAINTAIN COORDINATES OFFSET INFORMATION 
	indexes = {}
	# CREATE VERTEX
	for e in lattice.GetKeys():
		d = lattice.GetConcept(e)
		if d.level not in indexes:
			indexes[d.level] = 1
		if True:
			#if len(d["objects"]) > 0 and d["stability"] >= staboffset:
			# VERTEX
			vertex[e] = graph.AddVertex()
			vertex_id_array.SetValue(vertex[e],vertex[e])
			# CALCULATE COORDINATES
			levels = lattice.GetNumberOfLevels()
			ylim = 2000
			y = ylim-(ylim*d.level/levels)
			xlim = 2000
			x = -1*xlim/2 + xlim * (indexes[d.level])/(len(lattice.GetConceptsByLevel(d.level))+1)		
			# print x,y,levels,d.level
			#ASSIGN VALUES
			points.InsertNextPoint(x,y,0.0)
			vertex_label_array.SetValue(vertex[e],str(d.GetCleanIntent(output='s')))
			if len(d.GetIntent()) > 0:
				size_vertex_array.SetValue(vertex[e],5)
			else:
				size_vertex_array.SetValue(vertex[e],1)
			print e
			if lattice.GetConcept(e).GetSupport() != 0:
				print e,lattice.GetConcept(e).GetSupport()
				size_vertex_array.SetValue(vertex[e],math.log(lattice.GetConcept(e).GetSupport()))
			else:
				size_vertex_array.SetValue(0.1)
			intent_array.SetValue(vertex[e],e)
			stab_vertex_array.SetValue(vertex[e],d.stability)
			sup_vertex_array.SetValue(vertex[e],d.GetSupport())
		indexes[d.level] += 1
	return vertex, points, vertex_id_array,vertex_label_array,stab_vertex_array,sup_vertex_array,intent_array,size_vertex_array


def create_lattice_edges(graph,vertex,lattice):
	print "Creating Edges..."
	edge_id_array = vtkIdTypeArray()
	edge_id_array.SetName("id")
	# CREATE EDGES
	d = 0
	for key in lattice.GetKeys():
		if key in vertex:
			for concept2 in lattice.GetConcept(key).GetChildren():
				e = concept2.id
				if e in vertex:
					a = graph.AddGraphEdge(vertex[key],vertex[e])
					#print a.GetId()
					edge_id_array.InsertValue(a.GetId(),a.GetId())
					
					d += 1
	return edge_id_array

